/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package operator.client;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import share.ErrorException;
import share.SocketCommunicator;

/**
 *
 * @author mire
 */
public class OperatorClient {

    final static private String confName = ".oc.conf";
    private String myAddress;
    private String operatorAddress;
    private int operatorPort;
    private String operatorName;
    private SocketCommunicator socComm;

    public OperatorClient(String operatorAddress, int operatorPort, String operatorName) {
        this.operatorAddress = operatorAddress;
        this.operatorPort = operatorPort;
        this.operatorName = operatorName;
        try {
            myAddress = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException ex) {
            myAddress = null;
            Logger.getLogger(OperatorClient.class.getName()).log(Level.SEVERE, null, ex);
        }
        backupConfiguration();
    }

    public OperatorClient() {
        restoreConfiguration();
        try {
            myAddress = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException ex) {
            myAddress = null;
            Logger.getLogger(OperatorClient.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public String getOperatorAddress() {
        return operatorAddress;
    }

    public String getLocalAddress() {
        return myAddress;
    }

    public String getOperatorName() {
        return operatorName;
    }

    public int getOperatorPort() {
        return operatorPort;
    }

    public void setOperatorAddress(String operatorAddress) {
        this.operatorAddress = operatorAddress;
    }

    public void setOperatorPort(int operatorPort) {
        this.operatorPort = operatorPort;
    }

    public boolean start() throws ErrorException, UnknownHostException, IOException, SocketException {
        this.socComm = new SocketCommunicator(new Socket(operatorAddress, operatorPort));
        registerSelf();

        return true;
    }

    public synchronized static boolean existsConfugration() {
        File f = new File(confName);
        return f.exists();
    }

    public final synchronized void backupConfiguration() {
        File f = new File(confName);
        if (f.exists()) {
            f.delete();
        }

        FileWriter fw = null;
        PrintWriter outp = null;
        try {
            fw = new FileWriter(f);
            outp = new PrintWriter(fw, true);

            outp.println(operatorName);
            outp.println(operatorAddress);
            outp.println(operatorPort);

        } catch (IOException ex) {
            Logger.getLogger(OperatorClient.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                if (outp != null) {
                    outp.close();
                }
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException ex) {
                Logger.getLogger(OperatorClient.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public final synchronized boolean restoreConfiguration() {
        File f = new File(confName);
        if (!f.exists()) {
            return false;
        }

        FileReader fr = null;
        LineNumberReader inr = null;
        try {
            fr = new FileReader(f);
            inr = new LineNumberReader(fr);

            operatorName = inr.readLine();
            operatorAddress = inr.readLine();
            operatorPort = Integer.parseInt(inr.readLine());

        } catch (IOException ex) {
            Logger.getLogger(OperatorClient.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                if (inr != null) {
                    inr.close();
                }
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException ex) {
                Logger.getLogger(OperatorClient.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return true;
    }

    private void registerSelf() throws IOException, ErrorException {
        String status = "REGISTRING_SELF";
        System.out.println(status);
        socComm.open();
        socComm.writeString("NEW_CLIENT");
        socComm.writeString(operatorName);
        status = socComm.readString();
        if (!status.equals("REQUEST_ACCEPTED")) {
            socComm.close();
            throw new ErrorException(status);
        }
        System.out.println(status);

        status = socComm.readString();
        System.out.println(status);
        socComm.close();
    }

    public String newUser() throws ErrorException, IOException {
        String status = null;
        String user = null;

        status = "SENDING_NEW_USER_REQUEST";
        System.out.println(status);
        this.socComm = new SocketCommunicator(new Socket(operatorAddress, operatorPort));
        socComm.open();
        socComm.writeString("NEW_USER");
        status = socComm.readString();
        if (!status.equals("REQUEST_ACCEPTED")) {
            socComm.close();
            throw new ErrorException(status);
        }
        System.out.println(status);

        status = "WAITING";
        System.out.println(status);

        status = socComm.readString();
        System.out.println(status);

        user = socComm.readString();
        socComm.close();

        return user;
    }

    public String newMultipleUsers(int usersNo) throws ErrorException, IOException,OutOfMemoryError {
        String status = null;
        String newUsers = null;

        status = "SENDING_NEW_MULTIPLE_USER_REQUEST";
        System.out.println(status);

        this.socComm = new SocketCommunicator(new Socket(operatorAddress, operatorPort));
        socComm.open();
        socComm.writeString("NEW_MULTIPLE_USERS");
        status = socComm.readString();
        if (!status.equals("REQUEST_ACCEPTED")) {
            socComm.close();
            throw new ErrorException(status);
        }
        System.out.println(status);

        socComm.writeInt(usersNo);

        status = "WAITING";
        System.out.println(status);

        status = socComm.readString();
        System.out.println(status);

        newUsers = socComm.readString();
        socComm.close();

        return printNewUsers(newUsers);
    }

    private String printNewUsers(String manyUsers) {
        String userIDs = "";
        String workingString = manyUsers.replace('{', ' ').replace('}', ' ').replaceAll(" ", "");
        String[] splited = workingString.split(",");
        for (int i = 0; i < splited.length; i++) {
            String[] userSplited = splited[i].split("=");
            if (i != 0) {
                userIDs += "\n";
            }
            userIDs += userSplited[0];
            userIDs += "\t";
            userIDs += userSplited[1];
        }
        return userIDs;
    }

    public boolean transfer(String oldOperatorName, int userID, String userNumber) throws IOException, ErrorException {
        String status = null;

        if (!numberOK(userNumber)) {
            throw new ErrorException("ERROR_NUMBER");
        }

        status = "SENDING_TRANSFER_REQUEST";
        System.out.println(status);

        this.socComm = new SocketCommunicator(new Socket(operatorAddress, operatorPort));
        socComm.open();
        socComm.writeString("TRANSFER");
        status = socComm.readString();
        if (!status.equals("REQUEST_ACCEPTED")) {
            socComm.close();
            throw new ErrorException(status);
        }
        System.out.println(status);

        socComm.writeString(oldOperatorName);
        socComm.writeInt(userID);
        socComm.writeString(userNumber);

        status = "WAITING";
        System.out.println(status);

        status = socComm.readString();
        if (!status.equals("JOB_GOT")) {
            socComm.close();
            throw new ErrorException(status);
        }
        System.out.println(status);
        socComm.close();

        return true;
    }

    public boolean multipleTransfer(String oldOperatorName, String fileName) throws IOException, ErrorException {
        String status = null;
        String multipleUsers = readMultipleUsersFile(fileName);

        if (multipleUsers == null) {
            status = "ERROR_EMPTY_FILE";
            System.out.println(status);
            throw new ErrorException(status);
        }
        status = "SENDING_MULTIPLE_TRANSFER_REQUEST";

        this.socComm = new SocketCommunicator(new Socket(operatorAddress, operatorPort));
        socComm.open();
        socComm.writeString("MULTIPLE_TRANSFER");
        status = socComm.readString();
        if (!status.equals("REQUEST_ACCEPTED")) {
            socComm.close();
            throw new ErrorException(status);
        }
        System.out.println(status);

        socComm.writeString(oldOperatorName);
        socComm.writeString(multipleUsers);

        status = "WAITING";
        System.out.println(status);

        status = socComm.readString();
        if (!status.equals("JOB_GOT")) {
            socComm.close();
            throw new ErrorException(status);
        }
        System.out.println(status);
        socComm.close();
        return true;
    }

    private String readMultipleUsersFile(String fileName) throws ErrorException,OutOfMemoryError {
        String returnString = null;
        FileReader fr = null;
        LineNumberReader inr = null;
        String line = null;
        try {
            fr = new FileReader(fileName);
            inr = new LineNumberReader(fr);

            while ((line = inr.readLine()) != null) {
               if (inr.getLineNumber() != 1) {
                   returnString += ",";
               } else {
                   returnString = "";
               }

               String[] splited = line.split("\t");
               if (splited.length != 2) {
                   throw new ErrorException("ERROR_NO_NUMBER");
               }
               try {
                   Long.parseLong(splited[0]);
                   Long.parseLong(splited[1]);
               } catch (NumberFormatException ex) {
                   throw new ErrorException("ERROR_NO_NUMBER");
               }
               returnString += splited[0];
               returnString += "=";
               returnString += splited[1];

//                 returnString = "";
//                 returnString += line;
            }

        } catch (IOException ex) {
            Logger.getLogger(OperatorClient.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                if (inr != null) {
                    inr.close();
                }
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException ex) {
                Logger.getLogger(OperatorClient.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return returnString;
    }

    public String getTransfersLog() throws IOException, ErrorException,OutOfMemoryError {
        String status = "SENDING_LOG_REQUEST";
        System.out.println(status);

        this.socComm = new SocketCommunicator(new Socket(operatorAddress, operatorPort));
        socComm.open();
        socComm.writeString("GET_TRANSFER_LOG");
        status = socComm.readString();
        if (!status.equals("REQUEST_ACCEPTED")) {
            socComm.close();
            throw new ErrorException(status);
        }
        System.out.println(status);

        status = "WAITING";
        System.out.println(status);

        String log = socComm.readString();

        if (socComm.readString().equals("LOG_SENT")) {
            status = "LOG_RECEIVED";
        }
        System.out.println(status);
        socComm.close();

        return log;
    }

    public boolean deleteUser(int userID, String userNumber) throws IOException, ErrorException {
        if (!numberOK(userNumber)) {
            throw new ErrorException("ERROR_NUMBER");
        }


        String status = "SENDING_DELETE_USER_REQUEST";
        System.out.println(status);

        this.socComm = new SocketCommunicator(new Socket(operatorAddress, operatorPort));
        socComm.open();
        socComm.writeString("DELETE_USER");
        socComm.writeInt(userID);
        socComm.writeString(userNumber);

        status = socComm.readString();
        if (!status.equals("REQUEST_ACCEPTED")) {
            socComm.close();
            throw new ErrorException(status);
        }
        System.out.println(status);

        status = "WAITING";
        System.out.println(status);

        status = socComm.readString();
        System.out.println(status);
        socComm.close();
        return true;
    }

    private boolean numberOK(String userNumber) {
        boolean check = false;
        String workNumber = userNumber.replaceAll("\\D", "");

        String regEx = "^\\d{9}";

        Pattern pattern = Pattern.compile(regEx);
        Matcher matcher = pattern.matcher(workNumber);

        check = matcher.find();


        return check;
    }
}
